package org.xq.softcup.mq.remoting.invoker.call;

import org.xq.softcup.mq.remoting.net.params.XQRpcFutureResponse;
import org.xq.softcup.mq.remoting.net.params.XQRpcResponse;
import org.xq.softcup.mq.util.XQRpcException;

import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;

public class XQRpcInvokeFuture implements Future {
    private XQRpcFutureResponse futureResponse;

    public XQRpcInvokeFuture(XQRpcFutureResponse futureResponse) {
        this.futureResponse = futureResponse;
    }

    public void stop() {
        this.futureResponse.removeInvokerFuture();
    }

    public boolean cancel(boolean mayInterruptIfRunning) {
        return this.futureResponse.cancel(mayInterruptIfRunning);
    }

    public boolean isCancelled() {
        return this.futureResponse.isCancelled();
    }

    public boolean isDone() {
        return this.futureResponse.isDone();
    }

    public Object get() throws ExecutionException, InterruptedException {
        try {
            return get(-1L, TimeUnit.MILLISECONDS);
        } catch (TimeoutException e) {
            throw new XQRpcException(e);
        }
    }

    public Object get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException {
        try {
            XQRpcResponse xqRpcResponse = this.futureResponse.get(timeout, unit);
            if (xqRpcResponse.getErrorMsg() != null) {
                throw new XQRpcException(xqRpcResponse.getErrorMsg());
            }
            return xqRpcResponse.getResult();
        } finally {
            stop();
        }
    }

    private static ThreadLocal<XQRpcInvokeFuture> threadInvokerFuture = new ThreadLocal();

    public static <T> Future<T> getFuture(Class<T> type) {
        Future<T> future = (Future) threadInvokerFuture.get();
        threadInvokerFuture.remove();
        return future;
    }

    public static void setFuture(XQRpcInvokeFuture future) {
        threadInvokerFuture.set(future);
    }

    public static void removeFuture() {
        threadInvokerFuture.remove();
    }
}
